package com.ruiyian.netty.nettyTcp;

import com.ruiyian.common.constant.Constants;
import com.ruiyian.common.constant.HttpStatus;
import com.ruiyian.common.core.domain.AjaxResult;
import com.ruiyian.common.core.domain.entity.Point;
import com.ruiyian.common.core.redis.RedisCache;
import com.ruiyian.common.enums.ReturnType;
import com.ruiyian.common.utils.JSON;
import com.ruiyian.common.utils.jfinsUtil.FinsMessage;
import com.ruiyian.common.utils.jfinsUtil.FinsMessageUtils;
import com.ruiyian.common.utils.spring.SpringUtils;
import com.ruiyian.netty.nettyclient.NettyStarter;
import com.ruiyian.netty.nettyserver.MyChannelHandlerPool;
import com.ruiyian.project.equipment.service.PointService;
import com.ruiyian.project.plan.service.PlanService;
import com.ruiyian.system.domain.ServerEq;
import com.ruiyian.system.service.ServerService;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.handler.codec.http.websocketx.TextWebSocketFrame;
import io.netty.util.CharsetUtil;
import io.netty.util.ReferenceCountUtil;
import net.sf.json.JSONArray;
import net.sf.json.JSONObject;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.redisson.Redisson;
import org.redisson.api.RLock;

import java.net.InetAddress;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * @author chong
 * @email 673426164@qq.com
 * @create 2021/9/23
 * Desc:  tcp服务 业务处理
 */
public class NettyServerHandler extends ChannelInboundHandlerAdapter {

    private Log logger = LogFactory.getLog(NettyServerHandler.class);
    private static PlanService planService;
    private static RedisCache redisCache;
    private static PointService pointService;
    private static Redisson redisson;
    private static ServerService serverService;
    static {
        planService = SpringUtils.getBean(PlanService.class);
        redisCache = SpringUtils.getBean(RedisCache.class);
        pointService = SpringUtils.getBean(PointService.class);
        redisson = SpringUtils.getBean(Redisson.class);
        serverService = SpringUtils.getBean(ServerService.class);
    }
    //收到数据时调用
    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        try {
            ByteBuf in = (ByteBuf) msg;
            int readableBytes = in.readableBytes();
            byte[] bytes = new byte[readableBytes];
            in.readBytes(bytes);
            String str = new String(bytes);
            logger.info("接收到消息:" + str);
            JSONObject json = JSONObject.fromObject(str);
            //System.out.print(in.toString(CharsetUtil.UTF_8));
            String operateType = "";
            FinsMessage finsMessage = null;
            if (json.getString("type").equals("close")) {
                // 客户端关闭连接,websocket移除连接
                MyChannelHandlerPool.tcpChannelGroup.remove(ctx.channel());
                return;
            }
            String lockKey = "client_port_model";
            RLock redissonLock = redisson.getLock(lockKey);
            // 根据隧道获取点位列表 1仅读,2读写3,仅写
            List<Point> data = pointService.selectList(new Point(json.getLong("tunnelId"), json.containsKey("holeId") ? json.getLong("holeId") : null, json.containsKey("holeId") ? json.getLong("moduleId") : null, json.containsKey("groupNum") ? json.getInt("groupNum") : 0,0));
            // 统计连接PLC服务状态
            Map<Long, List<Point>> collectMap = data.stream().collect(Collectors.groupingBy(p -> p.getServerId()));
            List<ServerEq> serverList = new ArrayList<>();
            Map<String, ChannelFuture> connectMap = NettyStarter.getConnectMap();
            for (Long key : collectMap.keySet()) {
                ServerEq one = serverService.findOne(key);
                ChannelFuture channelFuture = connectMap.get(one.getHost() + ":" + one.getPort());
                if (channelFuture != null && channelFuture.isSuccess()) {
                    one.setIfOnline(1);
                } else {
                    one.setIfOnline(0);
                    // 把不在线的数据清除
                    data = data.stream().filter(p -> !p.getServerId().equals(key)).collect(Collectors.toList());
                }
                serverList.add(one);
            }
            List<Point> pointList = new ArrayList<>();
            if (json.getString("type").equals(ReturnType.QUERY.getCode())) {
                operateType = ReturnType.QUERY.getCode();
                // 推送当前服务状态
                Map<String, Object> map = new HashMap<>();
                map.put("operateType", ReturnType.ONLINE.getCode());
                map.put("data", serverList);
                map.put("tunnelId", json.getLong("tunnelId"));
                map.put("groupNum", json.containsKey("groupNum") ? json.getInt("groupNum") : 0);
                map.put(AjaxResult.CODE_TAG, HttpStatus.SUCCESS);
                ctx.channel().writeAndFlush(new TextWebSocketFrame(JSON.stringify(map)));
                // 获取可读点位
                pointList = data.stream().filter(f -> f.getStatus() != 3).collect(Collectors.toList());
                if (pointList.size() > 0) {
                    finsMessage = FinsMessageUtils.createReadFineMessage(pointList.get(0).getMemoryArea(), Integer.parseInt(pointList.get(0).getRegisterAddress()), pointList.get(0).getLength(), pointList.get(0).getServerPath());
                    NettyStarter.setServiceChannel(ctx.channel());
                }

            } else if (json.getString("type").equals(ReturnType.WRITE.getCode())) {
                operateType = ReturnType.WRITE.getCode();
                List<Point> writePointList = data.stream().filter(p -> p.getStatus() != 1).collect(Collectors.toList());
                if (json.containsKey("eqId") && StringUtils.isNotBlank(json.getString("eqId"))) {
                    Long eqId = Long.parseLong(json.getString("eqId"));
                    // 获取设备写入点位
                    writePointList = writePointList.stream().filter(p -> p.getEqId() != null && p.getEqId().equals(eqId)).collect(Collectors.toList());
                }
                JSONArray wrPoint = json.getJSONArray("value");
                for (int i = 0; i < wrPoint.size(); i++) {
                    JSONObject y = wrPoint.getJSONObject(i);
                    String pointCode = y.getString("pointCode");
                    // 同一设备不同类型写点位只有一个
                    List<Point> pointCodeList = writePointList.stream().filter(p -> p.getCode().equals(pointCode)).collect(Collectors.toList());
                    if (pointCodeList.size() > 0) {
                        Point point = pointCodeList.get(0);
                        point.setValue(y.getString("value"));
                        pointList.add(point);
                    }
                    // 预案
                    if (StringUtils.isNotBlank(pointCode) && pointCode.equals("plan")) {
                        planService.createLog(y.getLong("value"));
                    }
                }
                // 设备点位写入
                if (pointList.size() > 0) {
                    Integer writeValue = Integer.valueOf(pointList.get(0).getValue());
                    if (pointList.get(0).getType().equals("0")) {
                        finsMessage = FinsMessageUtils.createWriteMessage(pointList.get(0).getMemoryArea(), Integer.parseInt(pointList.get(0).getRegisterAddress()), writeValue, pointList.get(0).getServerPath());
                    } else {
                        // 位写入
                        finsMessage = FinsMessageUtils.createWriteMessage(pointList.get(0).getMemoryArea(), Integer.parseInt(pointList.get(0).getRegisterAddress()), pointList.get(0).getBits(), writeValue, pointList.get(0).getServerPath());
                    }
                }
            }
            if (pointList.size() > 0) {
                // 锁  缓存redis
                redissonLock.lock(3, TimeUnit.SECONDS);
                redisCache.setCacheObject(Constants.NOWINDEX, operateType.equals(ReturnType.QUERY.getCode()) ? 0 : 1); // 读取数据下标位
                // 返回数据
                redisCache.deleteObject(Constants.RETURNPOINT);
                // 操作点位集合
                redisCache.deleteObject(Constants.POINTLIST);
                redisCache.setCacheList(Constants.POINTLIST, pointList);
                redisCache.setCacheObject("groupNum", json.containsKey("groupNum") ? json.getInt("groupNum") : 0);
                redisCache.setCacheObject("tunnelId", json.getLong("tunnelId"));
                ChannelFuture channelFuture = connectMap.get(pointList.get(0).getServerPath());
                if (channelFuture != null && channelFuture.isSuccess()) {
                    channelFuture.channel().writeAndFlush(finsMessage.toString()).sync();
                }
            } else {
                json.put("operateType", operateType);
                json.put(AjaxResult.CODE_TAG, HttpStatus.BAD_REQUEST);
                json.put(AjaxResult.MSG_TAG, "plc连接失败");
                json.put("tunnelId", json.getLong("tunnelId"));
                ctx.channel().writeAndFlush(new TextWebSocketFrame(json.toString()));
                MyChannelHandlerPool.tcpChannelGroup.writeAndFlush(Unpooled.wrappedBuffer(JSON.stringify(json).getBytes(CharsetUtil.UTF_8)));
            }

        }catch (Exception e){
            logger.error(e.getMessage());
        }finally {
            ReferenceCountUtil.release(msg);
        }
    }

    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        // 当出现异常就关闭连接
        cause.printStackTrace();
        ctx.close();
    }

    /**
     * 建立连接时，返回消息
     * @param ctx
     * @throws Exception
     */
    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        //System.out.println("连接的客户端地址:" + ctx.channel().remoteAddress());
        logger.error("连接的客户端地址:" + ctx.channel().remoteAddress());
        logger.error("连接的客户端ID:" + ctx.channel().id());
        ctx.writeAndFlush("client" + InetAddress.getLocalHost().getHostName() + "success connected！ \n");
        System.out.println("connection");
        MyChannelHandlerPool.tcpChannelGroup.add(ctx.channel());
        //StaticVar.ctxList.add(ctx);
        //StaticVar.chc = ctx;
        super.channelActive(ctx);
    }

    /**
     * 断开连接
     * @param ctx
     * @throws Exception
     */
    @Override
    public void channelInactive(ChannelHandlerContext ctx) throws Exception {
        logger.error("断开:" + ctx.channel().id());
        MyChannelHandlerPool.tcpChannelGroup.remove(ctx.channel());
    }

}
